module divider (
    input clk,
    input rst,

    input i_valid,
    output logic i_ready,

    input res_rem,
    input src_word,
    input src_sign,
    input [63:0] src1,
    input [63:0] src2,

    output logic o_valid,
    input o_ready,
    output [63:0] res
);

  logic [127:0] rem;
  logic [ 63:0] quo;
  logic [127:0] divisor;
  logic [  6:0] cnt;
  logic quo_sign, rem_sign;
  logic res_w;
  logic res_sel_rem;
  wire src1_ltz = src_sign & (src_word ? src1[31] : src1[63]);
  wire src2_ltz = src_sign & (src_word ? src2[31] : src2[63]);
  wire [63:0] quotient;
  wire [63:0] remainder;
  always @(posedge clk) begin
    if (rst) begin
      quo <= 64'b0;
      rem <= 128'b0;
      divisor <= 128'b0;
      cnt <= 7'b0;
      quo_sign <= 1'b0;
      rem_sign <= 1'b0;
      o_valid <= 1'b0;
      i_ready <= 1'b1;
      res_w <= 1'b0;
      res_sel_rem <= 1'b0;
    end else begin
      quo <= 64'b0;
      rem <= 128'b0;
      o_valid <= 1'b0;
      if (i_valid & i_ready) begin
        i_ready <= 1'b0;
        quo <= 64'b0;
        res_w <= src_word;
        res_sel_rem <= res_rem;
        if (src_word) begin
          if (src_sign & src1[31]) rem <= {96'b0, {(~src1[31:0]) + 32'b1}};
          else rem <= {96'b0, src1[31:0]};
          if (src_sign & src2[31]) divisor <= {33'b0, 32'b0, {(~src2[31:0]) + 32'b1}, 31'b0};
          else divisor <= {33'b0, 32'b0, src2[31:0], 31'b0};
          cnt <= 7'd32;
        end else begin
          if (src_sign & src1[63]) rem <= {64'b0, (~src1) + 64'b1};
          else rem <= {64'b0, src1};
          if (src_sign & src2[63]) divisor <= {1'b0, {(~src2) + 64'b1}, 63'b0};
          else divisor <= {1'b0, src2, 63'b0};
          cnt <= 7'd64;
        end
        if (src1_ltz != src2_ltz) quo_sign <= 1'b1;
        else quo_sign <= 1'b0;
        rem_sign <= src1_ltz;
      end else if (i_ready == 1'b0 && o_ready) begin
        quo[63:1] <= quo[62:0];
        if (rem >= divisor) begin
          rem <= rem - divisor;
          quo[0] <= 1'b1;
        end else begin
          rem <= rem;
          quo[0] <= 1'b0;
        end
        divisor <= divisor >> 1;
        cnt <= cnt - 7'd1;
        if (cnt == 7'd1) begin
          i_ready <= 1'b1;
          o_valid <= 1'b1;
        end
      end
    end
  end
  wire [63:0] quotient_out = quo_sign ? ((~quo) + 64'b1) : quo;
  wire [63:0] remainder_out = rem_sign ? ((~rem[63:0]) + 64'b1) : rem[63:0];
  assign quotient[31:0] = quotient_out[31:0];
  assign quotient[63:32] = res_w ? {32{quotient_out[31]}} : quotient_out[63:32];
  assign remainder[31:0] = remainder_out[31:0];
  assign remainder[63:32] = res_w ? {32{remainder_out[31]}} : remainder_out[63:32];
  assign res = res_sel_rem ? remainder : quotient;
endmodule
